package me.leon.ext.android

import androidx.annotation.RawRes
import java.io.*
import java.nio.charset.Charset
import me.leon.ext.Utils.app

/**
 * <pre> author: Blankj blog : http://blankj.com time : 2018/05/07 desc : utils about resource
 * </pre> *
 */
object ResourceUtils {

    /**
     * Return the id identifier by name.
     *
     * @param name The name of id.
     * @return the id identifier by name
     */
    fun getIdByName(name: String?) = app.resources.getIdentifier(name, "id", app.packageName)

    /**
     * Return the string identifier by name.
     *
     * @param name The name of string.
     * @return the string identifier by name
     */
    fun getStringIdByName(name: String?) =
        app.resources.getIdentifier(name, "string", app.packageName)

    /**
     * Return the color identifier by name.
     *
     * @param name The name of color.
     * @return the color identifier by name
     */
    fun getColorIdByName(name: String?) =
        app.resources.getIdentifier(name, "color", app.packageName)

    /**
     * Return the dimen identifier by name.
     *
     * @param name The name of dimen.
     * @return the dimen identifier by name
     */
    fun getDimenIdByName(name: String?) =
        app.resources.getIdentifier(name, "dimen", app.packageName)

    /**
     * Return the drawable identifier by name.
     *
     * @param name The name of drawable.
     * @return the drawable identifier by name
     */
    fun getDrawableIdByName(name: String?) =
        app.resources.getIdentifier(name, "drawable", app.packageName)

    /**
     * Return the mipmap identifier by name.
     *
     * @param name The name of mipmap.
     * @return the mipmap identifier by name
     */
    fun getMipmapIdByName(name: String?) =
        app.resources.getIdentifier(name, "mipmap", app.packageName)

    /**
     * Return the layout identifier by name.
     *
     * @param name The name of layout.
     * @return the layout identifier by name
     */
    fun getLayoutIdByName(name: String?) =
        app.resources.getIdentifier(name, "layout", app.packageName)

    /**
     * Return the style identifier by name.
     *
     * @param name The name of style.
     * @return the style identifier by name
     */
    fun getStyleIdByName(name: String?) =
        app.resources.getIdentifier(name, "style", app.packageName)

    /**
     * Return the anim identifier by name.
     *
     * @param name The name of anim.
     * @return the anim identifier by name
     */
    fun getAnimIdByName(name: String?) = app.resources.getIdentifier(name, "anim", app.packageName)

    /**
     * Return the menu identifier by name.
     *
     * @param name The name of menu.
     * @return the menu identifier by name
     */
    fun getMenuIdByName(name: String?) = app.resources.getIdentifier(name, "menu", app.packageName)

    /**
     * Copy the file from assets.
     *
     * @param assetsFilePath The path of file in assets.
     * @param destFilePath The path of destination file.
     * @return `true`: success<br></br>`false`: fail
     */
    fun copyFileFromAssets(assetsFilePath: String, destFilePath: String): Boolean {
        var res = true
        try {
            val assets = app.assets.list(assetsFilePath)
            if (assets!!.isNotEmpty()) {
                for (asset in assets) {
                    res =
                        res and copyFileFromAssets("$assetsFilePath/$asset", "$destFilePath/$asset")
                }
            } else {
                res = writeFileFromIS(destFilePath, app.assets.open(assetsFilePath), false)
            }
        } catch (e: IOException) {
            e.printStackTrace()
            res = false
        }
        return res
    }

    /**
     * Return the content of assets.
     *
     * @param assetsFilePath The path of file in assets.
     * @return the content of assets
     */
    @JvmOverloads
    fun readAssets2String(assetsFilePath: String?, charsetName: String? = null): String? {
        val inputStream =
            try {
                app.assets.open(assetsFilePath!!)
            } catch (e: IOException) {
                e.printStackTrace()
                return null
            }
        val bytes = is2Bytes(inputStream) ?: return null
        return if (charsetName.isNullOrEmpty()) {
            String(bytes)
        } else {
            try {
                String(bytes, Charset.forName(charsetName))
            } catch (e: UnsupportedEncodingException) {
                e.printStackTrace()
                ""
            }
        }
    }

    /**
     * Return the content of file in assets.
     *
     * @param assetsPath The path of file in assets.
     * @return the content of file in assets
     */
    @JvmOverloads
    fun readAssets2List(assetsPath: String?, charsetName: String? = null): List<String>? {
        return try {
            is2List(app.resources.assets.open(assetsPath!!), charsetName)
        } catch (e: IOException) {
            e.printStackTrace()
            null
        }
    }

    /**
     * Copy the file from raw.
     *
     * @param resId The resource id.
     * @param destFilePath The path of destination file.
     * @return `true`: success<br></br>`false`: fail
     */
    fun copyFileFromRaw(@RawRes resId: Int, destFilePath: String): Boolean {
        return writeFileFromIS(destFilePath, app.resources.openRawResource(resId), false)
    }

    /**
     * Return the content of resource in raw.
     *
     * @param resId The resource id.
     * @return the content of resource in raw
     */
    @JvmOverloads
    fun readRaw2String(@RawRes resId: Int, charsetName: String? = null): String? {
        val inputStream = app.resources.openRawResource(resId)
        val bytes = is2Bytes(inputStream) ?: return null
        return if (charsetName.isNullOrEmpty()) {
            String(bytes)
        } else {
            try {
                String(bytes, Charset.forName(charsetName))
            } catch (e: UnsupportedEncodingException) {
                e.printStackTrace()
                ""
            }
        }
    }

    /**
     * Return the content of resource in raw.
     *
     * @param resId The resource id.
     * @return the content of file in assets
     */
    @JvmOverloads
    fun readRaw2List(@RawRes resId: Int, charsetName: String? = null): List<String>? {
        return is2List(app.resources.openRawResource(resId), charsetName)
    }

    ///////////////////////////////////////////////////////////////////////////
    // other utils methods
    ///////////////////////////////////////////////////////////////////////////
    private fun writeFileFromIS(
        filePath: String?,
        inputStream: InputStream,
        append: Boolean
    ): Boolean {
        return writeFileFromIS(getFileByPath(filePath), inputStream, append)
    }

    private fun writeFileFromIS(file: File?, inputStream: InputStream?, append: Boolean): Boolean {

        if (!createOrExistsFile(file) || inputStream == null) return false

        return try {
            inputStream.buffered().use { input ->
                FileOutputStream(file, append).buffered().use { output ->
                    var read: Int
                    while (input.read().also { read = it } != -1) output.write(read)
                }
            }
            true
        } catch (e: Exception) {
            e.printStackTrace()
            false
        }
    }

    private fun getFileByPath(filePath: String?): File? {
        return if (filePath.isNullOrEmpty()) null else File(filePath)
    }

    private fun createOrExistsFile(file: File?): Boolean {
        if (file == null) return false
        if (file.exists()) return file.isFile
        return if (!createOrExistsDir(file.parentFile)) false
        else
            try {
                file.createNewFile()
            } catch (e: IOException) {
                e.printStackTrace()
                false
            }
    }

    private fun createOrExistsDir(file: File?): Boolean {
        return file != null && if (file.exists()) file.isDirectory else file.mkdirs()
    }

    private fun is2Bytes(inputStream: InputStream?) =
        try {
            inputStream?.readBytes()
        } catch (ignored: Exception) {
            null
        }

    private fun is2List(inputStream: InputStream, charsetName: String? = Charsets.UTF_8.name()) =
        try {
            inputStream
                .bufferedReader(Charset.forName(charsetName ?: Charsets.UTF_8.name()))
                .readLines()
        } catch (ignored: Exception) {
            null
        }
}
